//////////////////////////////////// 云平台交互packet ////////////////////////////////////

use byteorder::{ByteOrder, NetworkEndian};
use bytes::{BufMut, Bytes, BytesMut};
use serde::{Deserialize, Serialize};
use crate::cloud_pkt::CPPayload::VLoginResp;
use crate::data_upload::upload_pkt::UploadPayloadItem;
use crate::defs::{CLOUD_PKT_MIN_LEN, CloudCmdType, EncryptionType, PayloadFormat, ResponseType};
use crate::util;

use crate::error::Error;


#[derive(Debug, Clone)]
pub enum CPPayload {
    /// 车辆登入-请求
    VLoginReq(VehicleLoginReqPayload),
    /// 车辆登入-响应
    VLoginResp(VehicleLoginRespPayload),
    /// 上报数据
    UploadRTDataReq(RTDataUploadReqPayload),
    /// 心跳数据-请求
    HeartbeatReq,
    /// 校准时间-请求
    TimeCalReq,
    None,
}

impl PayloadFormat for CPPayload {
    fn serialize(&mut self) -> BytesMut {
        let ret_bmt = BytesMut::zeroed(0);
        return match self {
            Self::VLoginReq(v) => { v.serialize() }
            Self::VLoginResp(v) => { v.serialize() }
            Self::UploadRTDataReq(v) => { v.serialize() }
            Self::HeartbeatReq | Self::TimeCalReq => { ret_bmt }
            Self::None => { ret_bmt }
        };
    }

    fn get_len(&mut self) -> usize {
        return match self {
            Self::VLoginReq(v) => { v.get_len() }
            Self::VLoginResp(v) => { v.get_len() }
            Self::UploadRTDataReq(v) => { v.get_len() }
            Self::HeartbeatReq | Self::TimeCalReq => { 0 }
            Self::None => { 0 }
        };
    }
}


///
/// 车辆登入payload - Chapter 7.1
///
#[repr(C)]
#[derive(Debug, Clone)]
pub struct VehicleLoginReqPayload {
    pub data_collect_time: [u8; 6],
    pub login_sid: u16,
    pub sim_iccid: [u8; 20],
    /// 可充电储能子系统数
    pub subsys_count: u8,
    pub sussys_encoding_len: u8,
    pub sys_encodings: Vec<u8>,
}

impl PayloadFormat for VehicleLoginReqPayload {
    fn serialize(&mut self) -> BytesMut {
        let mut b = BytesMut::with_capacity(self.get_len());
        b.put_slice(&self.data_collect_time[..]);

        let mut buf = [0; 2];
        NetworkEndian::write_u16(&mut buf, self.login_sid);
        b.put_slice(buf.as_slice());

        // b.put_u16(self.login_sid);
        b.put_slice(&self.sim_iccid[..]);
        b.put_u8(self.subsys_count);
        b.put_u8(self.sussys_encoding_len);
        b.put_slice(&self.sys_encodings[..]);
        b
    }
    fn get_len(&mut self) -> usize {
        (6 + 2 + 20 + 1 + 1 + self.sys_encodings.len()) as usize
    }
}

impl TryFrom<CPPayload> for VehicleLoginReqPayload {
    type Error = Error;

    fn try_from(src: CPPayload) -> Result<Self, Self::Error> {
        match src {
            CPPayload::VLoginReq(val) => Ok(val),
            _ => { Err(Error::TypeMismatch) }
        }
    }
}

///
/// 车辆登入响应payload
///
#[repr(C)]
#[derive(Debug, Clone)]
pub struct VehicleLoginRespPayload {
    pub data_collect_time: [u8; 6],
}

impl PayloadFormat for crate::cloud_pkt::VehicleLoginRespPayload {
    fn serialize(&mut self) -> BytesMut {
        let mut b = BytesMut::with_capacity(self.get_len());
        b.put_slice(&self.data_collect_time[..]);
        b
    }
    fn get_len(&mut self) -> usize {
        6
    }
}

impl TryFrom<CPPayload> for crate::cloud_pkt::VehicleLoginRespPayload {
    type Error = Error;

    fn try_from(src: CPPayload) -> Result<Self, Self::Error> {
        match src {
            CPPayload::VLoginResp(val) => Ok(val),
            _ => { Err(Error::TypeMismatch) }
        }
    }
}

///
/// 实时上报payload - Chapter 7.2
///
#[repr(C)]
#[derive(Debug, Clone)]
pub struct RTDataUploadReqPayload {
    pub data_collect_time: [u8; 6],
    pub upload_data_vec: Vec<UploadPayloadItem>,
    payload_len: usize,
}

impl PayloadFormat for RTDataUploadReqPayload {
    fn serialize(&mut self) -> BytesMut {
        let mut b = BytesMut::with_capacity(self.get_len());
        b.put_slice(&self.data_collect_time[..]);
        self.payload_len = self.payload_len + 6;

        for upload_data in &self.upload_data_vec {
            if upload_data.frame.is_none() {
                continue;
            }
            b.put_u8(upload_data.type_id);
            let pb = upload_data.frame.as_ref().unwrap();
            self.payload_len = self.payload_len + pb.clone().get_len();
            b.put_slice(pb.clone().serialize().as_ref());
        }
        // log::info!("RTDataUploadReqPayload(len:{:?}) is {:?}", self.payload_len, b);
        b
    }
    fn get_len(&mut self) -> usize {
        (6 + self.payload_len) as usize
    }
}

impl TryFrom<CPPayload> for RTDataUploadReqPayload {
    type Error = Error;

    fn try_from(src: CPPayload) -> Result<Self, Self::Error> {
        match src {
            CPPayload::UploadRTDataReq(val) => Ok(val),
            _ => { Err(Error::TypeMismatch) }
        }
    }
}

impl RTDataUploadReqPayload {
    pub fn new() -> Self {
        Self {
            data_collect_time: [u8::default(); 6],
            upload_data_vec: vec![],
            payload_len: 0,
        }
    }

    pub fn append_item(&mut self, item: &UploadPayloadItem) {
        self.upload_data_vec.push(item.clone());
        self.payload_len = item.clone().frame.unwrap().get_len();
    }
}


///////////////////////////////////////////////////////////////////////////////////////////////////

///
/// 云平台交互协议头
///
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ProtoCloudHeader {
    /// frame 起始符 为0x23,0x23, string为“##”
    pub f_start: u16,
    pub cmd_id: u8,
    pub resp_flag: u8,
    pub vin_id: [u8; 17],
    pub encrypt_type: u8,
    pub payload_len: u16,
}

impl PayloadFormat for ProtoCloudHeader {
    fn serialize(&mut self) -> BytesMut {
        let mut b = BytesMut::with_capacity(self.get_len());
        b.put_u16(self.f_start);
        b.put_u8(self.cmd_id);
        b.put_u8(self.resp_flag);
        b.put_slice(&self.vin_id[..]);
        b.put_u8(self.encrypt_type);
        b.put_u16(self.payload_len);
        // b.put_slice(&self.ble_payload[..]);
        b
    }
    fn get_len(&mut self) -> usize {
        (2 + 1 + 1 + 17 + 1 + 2) as usize
    }
}

impl TryFrom<Bytes> for ProtoCloudHeader {
    type Error = Error;

    fn try_from(src: Bytes) -> Result<Self, Self::Error> {
        if src.len() != Self::get_size() as usize {
            return Err(Error::InvalidPacket);
        }
        let payload_len = NetworkEndian::read_u16(&src.slice(22..24));
        let header = Self {
            f_start: NetworkEndian::read_u16(&src.slice(0..2)),
            cmd_id: src[2],
            resp_flag: src[3],
            vin_id: <[u8; 17]>::try_from(src.slice(4..21).to_vec().as_slice()).unwrap(),
            encrypt_type: src[21],
            payload_len,
        };
        // 类型检测
        match CloudCmdType::try_from(header.cmd_id) {
            Ok(_) => {}
            Err(_) => {
                return Err(Error::CmdIDMismatch(header.cmd_id));
            }
        };
        Ok(header)
    }
}

impl ProtoCloudHeader {
    pub fn new() -> Self {
        Self {
            f_start: 0x2323,
            cmd_id: 0,
            resp_flag: ResponseType::Command as u8,
            vin_id: [u8::default(); 17],
            encrypt_type: EncryptionType::None.into(),
            payload_len: 0,
        }
    }
    /// 获取header的大小
    pub fn get_size() -> usize {
        (2 + 1 + 1 + 17 + 1 + 2) as usize
    }

    pub fn pkt_len(&self) -> usize {
        (2 + 1 + 1 + 17 + 1 + 2) as usize
    }

    pub fn to_bytes(&self) -> Bytes {
        let mut b = BytesMut::with_capacity(8);

        b.put_u16(self.f_start);
        b.put_u8(self.cmd_id);
        b.put_u8(self.resp_flag);
        b.put_slice(&self.vin_id[..]);
        b.put_u8(self.encrypt_type);
        let mut buf = [0; 2];
        NetworkEndian::write_u16(&mut buf, self.payload_len);
        b.put_slice(buf.as_slice());
        b.freeze()
    }
}

///
/// 云平台协议packet
///
#[repr(C)]
#[derive(Debug, Clone)]
pub struct CloudPacket {
    pub header: ProtoCloudHeader,
    pub payload: Option<CPPayload>,
    pub crc: u8,

    // 以下为本地统计使用,非packet内容
    /// 重试统计次数
    pub retry_cnt: u8,
}

impl CloudPacket {
    pub fn new() -> Self {
        Self {
            header: ProtoCloudHeader::new(),
            payload: None,
            crc: 0,
            retry_cnt: 0,
        }
    }
    /// 设置该packet的payload
    pub fn set_payload(&mut self, payload: Option<CPPayload>) {
        self.payload = payload;
        // 同时更新header相关字段
        if self.payload.is_none() {
            return;
        }
        let payload_len = self.payload.as_mut().unwrap().get_len();
        self.header.payload_len = payload_len as u16;
    }

    pub fn to_bytes_mut(&mut self) -> BytesMut {
        let mut buf = BytesMut::with_capacity(self.header.pkt_len());
        if self.payload.is_none() {
            // 无payload
            self.crc = 0;
            buf.extend(self.header.to_bytes());
        } else {
            // 存在payload
            let pb = self.payload.as_mut().unwrap().serialize().freeze();
            self.header.payload_len = pb.len() as u16;

            buf.extend(self.header.to_bytes());
            buf.extend_from_slice(pb.as_ref());
        }

        // 计算crc (BOC 异或)
        self.crc = util::hash_crc8(&buf);
        buf.put_u8(self.crc);
        log::debug!("Packet calc CRC is {:02x}\n", self.crc);

        buf
    }
    pub fn to_bytes(&mut self) -> Bytes {
        self.to_bytes_mut().freeze()
    }
}

/// 平台包检查
pub fn cloud_proto_packet_check(content: &bytes::Bytes) -> Result<(ProtoCloudHeader, CloudCmdType, u8), Error> {
    if content.len() < CLOUD_PKT_MIN_LEN as usize {
        log::error!("Packet is too small");
        return Err(Error::InvalidPacket);
    }
    let content_copy = content.slice(0..content.len() - 1);
    let next_offset_position = ProtoCloudHeader::get_size();
    let header_start = content_copy.slice(0..next_offset_position);
    let payload_start = content_copy.slice(next_offset_position..);
    let pkt_crc = content[content.len() - 1];
    let header_ret = ProtoCloudHeader::try_from(header_start);
    if header_ret.is_err() {
        log::error!("header convert failed");
        return Err(header_ret.unwrap_err());
    }
    let pkt_header = header_ret.unwrap();
    // 检测payload checksum
    let calc_crc8 = util::hash_crc8_bytes(&content_copy);
    if calc_crc8 != pkt_crc {
        log::error!("Crc check failed, 0x{:02x}(calc) != 0x{:02x}(pkt)", calc_crc8, pkt_crc);
        return Err(Error::ChecksumMismatch);
    }
    // 检测payload长度是否等于header字段
    if pkt_header.payload_len != (payload_start.len() as u16) {
        return Err(Error::PayloadLenMismatch(pkt_header.payload_len, payload_start.len() as u16));
    }

    let cmd_id_enum = match CloudCmdType::try_from(pkt_header.cmd_id) {
        Ok(v) => { v }
        Err(_err) => {
            return Err(Error::CmdIDMismatch(pkt_header.cmd_id));
        }
    };
    Ok((pkt_header, cmd_id_enum, pkt_crc))
}

/// 平台Packet解析 Bytes转结构体
impl TryFrom<Bytes> for CloudPacket {
    type Error = Error;

    fn try_from(src: Bytes) -> Result<Self, Self::Error> {
        let check_ret = cloud_proto_packet_check(&src);
        if check_ret.is_err() {
            log::error!("cloud_proto_packet_check failed");
            return Err(check_ret.unwrap_err());
        }
        let (proto_header, cmd_type, crc) = check_ret.unwrap();
        let header_size = ProtoCloudHeader::get_size();
        match cmd_type {
            CloudCmdType::VehicleLogin => {
                let mut pkt = CloudPacket::new();
                pkt.header = proto_header;
                pkt.crc = crc;
                let d_time = <[u8; 6]>::try_from(src.slice(header_size..header_size + 6).as_ref());
                if d_time.is_err() {
                    log::error!("field data_collect_time is invalid");
                    return Err(Error::InvalidPacket);
                }
                let p_req = VehicleLoginRespPayload {
                    data_collect_time: d_time.unwrap(),
                };
                pkt.set_payload(Some(VLoginResp(p_req)));
                return Ok(pkt);
            }
            CloudCmdType::RealTimeInfoUpload => {
                return Err(Error::TypeNotSupport);
            }
            CloudCmdType::HistoryInfoUpload => {
                return Err(Error::TypeNotSupport);
            }
            CloudCmdType::VehicleLogout => {
                return Err(Error::TypeNotSupport);
            }
            CloudCmdType::PlatformLogin => {
                return Err(Error::TypeNotSupport);
            }
            CloudCmdType::PlatformLogout => {
                return Err(Error::TypeNotSupport);
            }
            CloudCmdType::VehicleHeartbeat => {
                return Err(Error::TypeNotSupport);
            }
            CloudCmdType::VehicleTimeCal => {
                return Err(Error::TypeNotSupport);
            }
            CloudCmdType::UserCustomA => {
            return Err(Error::TypeNotSupport);
            }
            CloudCmdType::ServerQuery => {
                return Err(Error::TypeNotSupport);
            }
            CloudCmdType::ServerSetup => {
                return Err(Error::TypeNotSupport);
            }
            CloudCmdType::ServerCtrl => {
                return Err(Error::TypeNotSupport);
            }
        }
    }
}